package com.bite.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.security.exception.ServiceException;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamQuestion;
import com.bite.system.domain.exam.dto.ExamAddDTO;
import com.bite.system.domain.exam.dto.ExamEditDTO;
import com.bite.system.domain.exam.dto.ExamQueryDTO;
import com.bite.system.domain.exam.dto.ExamQuestAddDTO;
import com.bite.system.domain.exam.vo.ExamDetailVO;
import com.bite.system.domain.exam.vo.ExamVO;
import com.bite.system.domain.question.Question;
import com.bite.system.domain.question.vo.QuestionVO;
import com.bite.system.manager.ExamCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamQuestionMapper;
import com.bite.system.mapper.question.QuestionMapper;
import com.bite.system.service.exam.IExamService;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.bite.common.core.constants.Constants.FALSE;
import static com.bite.common.core.constants.Constants.TRUE;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
      return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        checkExamSaveParams(examAddDTO,null);

        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }
    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if(exam.getStatus().equals(TRUE)) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSaveParams(examEditDTO, exam.getExamId());

        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        // 10.00   11.00
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<Question>().eq(Question::getQuestionId, questionId));
        if(CollectionUtil.isEmpty(questions)) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    @Override
    public int delete(Long examId) {
        // 先删竞赛中的题目 再删竞赛
        Exam exam = getExam(examId);
        if(exam.getStatus().equals(TRUE)) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw  new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        checkExam(exam);
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        if(count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(TRUE);

        //要将新竞赛的信息存入Redis中  e:t:l  e:d:examId
        examCacheManager.addCache(exam);

        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw  new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private void checkExam(Exam exam) {
       //只有已发布的竞赛才检查开始时间
        if(exam.getStatus() == 1 && exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId) {
        //竞赛名称标题不能重复 并且还有时间的判断
        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examSaveDTO.getTitle())
                .ne(examId!=null, Exam::getExamId, examId));
        if(CollectionUtil.isNotEmpty(exams)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if(examSaveDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    @Override
    public boolean questionAdd(ExamQuestAddDTO examQuestAddDTO) {

        Exam exam = getExam(examQuestAddDTO.getExamId());
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        Set<Long> questionIdSet = examQuestAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)) {
            return false ;
        }
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if(CollectionUtil.isEmpty(questionList) || questionList.size() != questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }

        // 新增：检查题目是否已存在于竞赛中
        List<ExamQuestion> existingQuestions = examQuestionMapper.selectList(
                new LambdaQueryWrapper<ExamQuestion>()
                        .eq(ExamQuestion::getExamId, examQuestAddDTO.getExamId())
                        .in(ExamQuestion::getQuestionId, questionIdSet)
        );

        if (CollectionUtil.isNotEmpty(existingQuestions)) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_ALREADY_EXISTS);
        }

        return saveExamQuestion(examQuestAddDTO, questionIdSet);
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        ExamDetailVO examDetailVO = new ExamDetailVO();

        Exam exam = getExam(examId);
        BeanUtil.copyProperties(exam,examDetailVO);
        List<QuestionVO> questionVOList = examQuestionMapper.selectExamQuestionList(examId);
        if (CollectionUtil.isEmpty(questionVOList)) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
/*        //查询出题目
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
        //无题目竞赛
        if(CollectionUtil.isEmpty(examQuestionList)) {
            return  examDetailVO;
        }
        //有题目 先获取出题目id
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        //根据题目Id 查询出题目信息
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
        //List<QuestionVO> questionVOList = new ArrayList<>();
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;*/
    }



    private boolean saveExamQuestion(ExamQuestAddDTO examQuestAddDTO, Set<Long> questionIdSet) {
        // 安全获取最大顺序号
        ExamQuestion maxOrderQuestion = examQuestionMapper.selectOne(
                new LambdaQueryWrapper<ExamQuestion>()
                        .eq(ExamQuestion::getExamId, examQuestAddDTO.getExamId())
                        .orderByDesc(ExamQuestion::getQuestionOrder)
                        .last("LIMIT 1")
        );

        Integer maxOrder = (maxOrderQuestion != null) ? maxOrderQuestion.getQuestionOrder() : null;
        int startOrder = (maxOrder == null) ? 1 : maxOrder + 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for(Long questionId : questionIdSet) {
//            Question question = questionMapper.selectById(questionId);
//            if(question == null) {
//                throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
//            }
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(examQuestAddDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(startOrder++);
            examQuestionList.add(examQuestion);
        }
        //一步完成  批量增加
        return saveBatch(examQuestionList);
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById( examId);
        if(exam == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }
}
